#include "sdk.h"
#include <wx/bitmap.h>
#include <wx/bmpbuttn.h>
#include <wx/statline.h>
#include <wx/combobox.h>
#include <wx/menu.h>
#include <wx/sizer.h>
#include <wx/splitter.h>
#include <wx/statbox.h>
#include <wx/stattext.h>
#include <wx/settings.h>
#include <wx/toolbar.h>
#include "configmanager.h"
#include "editorcolourset.h"
#include "infowindow.h"
#include "logmanager.h"
#include "M_Code_StyledTextCtrl.h"
#include "encodingdetector.h"
#include "SearchInPanel.h"
#include "DirectoryParamsPanel.h"
#include "ThreadSearch.h"
#include "ThreadSearchView.h"
#include "ThreadSearchEvent.h"
#include "ThreadSearchThread.h"
#include "ThreadSearchFindData.h"
#include "ThreadSearchConfPanel.h"
#include "ThreadSearchControlIds.h"
#include "wx/tglbtn.h"

const unsigned int MAX_NB_SEARCH_ITEMS = 20;
const int TIMER_PERIOD = 100;

ThreadSearchView::ThreadSearchView( ThreadSearch& threadSearchPlugin )
  : wxPanel( Manager::Get()->GetAppWindow() )
  , m_ThreadSearchPlugin( threadSearchPlugin )
  , m_Timer( this, controlIDs.Get( ControlIDs::idTmrListCtrlUpdate ) )
  , m_StoppingThread( 0 ) {
  m_pFindThread = NULL;
  m_pToolBar    = NULL;
  const wxString &prefix = GetImagePrefix();
  m_pSplitter = new wxSplitterWindow( this, -1, wxDefaultPosition, wxSize( 1, 1 ), wxSP_3D | wxSP_BORDER | wxSP_PERMIT_UNSPLIT );
  m_pPnlListLog = new wxPanel( m_pSplitter, -1, wxDefaultPosition, wxSize( 1, 1 ) );
  m_pPnlPreview = new wxPanel( m_pSplitter, -1, wxDefaultPosition, wxSize( 1, 1 ) );
  m_pSizerSearchDirItems_staticbox = new wxStaticBox( this, -1, _T( "条件参数" ) );
  const wxString m_pCboSearchExpr_choices[] = {

  };
  m_pCboSearchExpr = new wxComboBox( this, controlIDs.Get( ControlIDs::idCboSearchExpr ), wxEmptyString,
                                     wxDefaultPosition, wxDefaultSize, 0, m_pCboSearchExpr_choices,
                                     wxCB_DROPDOWN | wxTE_PROCESS_ENTER );
  m_pBtnSearch = new wxBitmapButton( this, controlIDs.Get( ControlIDs::idBtnSearch ),
                                     wxBitmap( prefix + _T( "findf.png" ), wxBITMAP_TYPE_PNG ),
                                     wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW );
  m_pBtnOptions = new wxBitmapButton( this, controlIDs.Get( ControlIDs::idBtnOptions ),
                                      wxBitmap( prefix + _T( "options.png" ), wxBITMAP_TYPE_PNG ),
                                      wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW );
  m_pStaticLine1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
  m_pStaTxtSearchIn = new wxStaticText( this, -1, _T( "范围: " ) );
  m_pPnlSearchIn = new SearchInPanel( this, -1 );
  m_pStaticLine2 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
  m_pBtnShowDirItems = new wxBitmapButton( this, controlIDs.Get( ControlIDs::idBtnShowDirItemsClick ),
      wxBitmap( prefix + _T( "showdir.png" ), wxBITMAP_TYPE_PNG ),
      wxDefaultPosition, wxDefaultSize, wxBU_AUTODRAW );
  m_pPnlDirParams = new DirectoryParamsPanel( &threadSearchPlugin.GetFindData(), this, -1 );
  m_pSearchPreview = new M_Code_StyledTextCtrl( m_pPnlPreview, wxID_ANY, wxDefaultPosition, wxSize( 1, 1 ) );
  m_pLogger = ThreadSearchLoggerBase::BuildThreadSearchLoggerBase( *this, m_ThreadSearchPlugin,
              m_ThreadSearchPlugin.GetLoggerType(), m_ThreadSearchPlugin.GetFileSorting(), m_pPnlListLog,
              controlIDs.Get( ControlIDs::idWndLogger ) );
  set_properties();
  do_layout();
  long id = m_pSearchPreview->GetId();
  Connect( id, wxEVT_SCI_MARGINCLICK,
           ( wxObjectEventFunction )( wxEventFunction )( wxScintillaEventFunction )
           &ThreadSearchView::OnMarginClick );
  Connect( id, wxEVT_CONTEXT_MENU,
           ( wxObjectEventFunction )( wxEventFunction )( wxContextMenuEventFunction )
           &ThreadSearchView::OnContextMenu );
  Connect( wxEVT_THREAD_SEARCH_ERROR,
           ( wxObjectEventFunction )&ThreadSearchView::OnThreadSearchErrorEvent );
}

ThreadSearchView::~ThreadSearchView() {
  if( m_pFindThread != NULL ) {
    StopThread();
  }
  long id = m_pSearchPreview->GetId();
  Disconnect( id, wxEVT_SCI_MARGINCLICK,
              ( wxObjectEventFunction )( wxEventFunction )( wxScintillaEventFunction )
              &ThreadSearchView::OnMarginClick );
  Disconnect( id, wxEVT_CONTEXT_MENU,
              ( wxObjectEventFunction )( wxEventFunction )( wxContextMenuEventFunction )
              &ThreadSearchView::OnContextMenu );
  Disconnect( wxEVT_THREAD_SEARCH_ERROR,
              ( wxObjectEventFunction )&ThreadSearchView::OnThreadSearchErrorEvent );
  m_ThreadSearchPlugin.OnThreadSearchViewDestruction();
  delete m_pLogger;
  m_pLogger = NULL;
}

BEGIN_EVENT_TABLE( ThreadSearchView, wxPanel )
  EVT_TEXT_ENTER( controlIDs.Get( ControlIDs::idCboSearchExpr ), ThreadSearchView::OnCboSearchExprEnter )
  EVT_TEXT_ENTER( controlIDs.Get( ControlIDs::idSearchDirPath ), ThreadSearchView::OnCboSearchExprEnter )
  EVT_TEXT_ENTER( controlIDs.Get( ControlIDs::idSearchMask ), ThreadSearchView::OnCboSearchExprEnter )
  EVT_BUTTON( controlIDs.Get( ControlIDs::idBtnSearch ), ThreadSearchView::OnBtnSearchClick )
  EVT_BUTTON( controlIDs.Get( ControlIDs::idBtnOptions ), ThreadSearchView::OnBtnOptionsClick )
  EVT_MENU( controlIDs.Get( ControlIDs::idOptionDialog ), ThreadSearchView::OnShowOptionsDialog )
  EVT_MENU( controlIDs.Get( ControlIDs::idOptionWholeWord ), ThreadSearchView::OnQuickOptions )
  EVT_MENU( controlIDs.Get( ControlIDs::idOptionStartWord ), ThreadSearchView::OnQuickOptions )
  EVT_MENU( controlIDs.Get( ControlIDs::idOptionMatchCase ), ThreadSearchView::OnQuickOptions )
  EVT_MENU( controlIDs.Get( ControlIDs::idOptionRegEx ), ThreadSearchView::OnQuickOptions )
  EVT_UPDATE_UI( controlIDs.Get( ControlIDs::idOptionWholeWord ), ThreadSearchView::OnQuickOptionsUpdateUI )
  EVT_UPDATE_UI( controlIDs.Get( ControlIDs::idOptionStartWord ), ThreadSearchView::OnQuickOptionsUpdateUI )
  EVT_UPDATE_UI( controlIDs.Get( ControlIDs::idOptionMatchCase ), ThreadSearchView::OnQuickOptionsUpdateUI )
  EVT_UPDATE_UI( controlIDs.Get( ControlIDs::idOptionRegEx ), ThreadSearchView::OnQuickOptionsUpdateUI )
  EVT_BUTTON( controlIDs.Get( ControlIDs::idBtnShowDirItemsClick ), ThreadSearchView::OnBtnShowDirItemsClick )
  EVT_SPLITTER_DCLICK( -1, ThreadSearchView::OnSplitterDoubleClick )
  EVT_TOGGLEBUTTON( controlIDs.Get( ControlIDs::idBtnSearchOpenFiles ), ThreadSearchView::OnBtnSearchOpenFiles )
  EVT_TOGGLEBUTTON( controlIDs.Get( ControlIDs::idBtnSearchTargetFiles ), ThreadSearchView::OnBtnSearchTargetFiles )
  EVT_TOGGLEBUTTON( controlIDs.Get( ControlIDs::idBtnSearchProjectFiles ), ThreadSearchView::OnBtnSearchProjectFiles )
  EVT_TOGGLEBUTTON( controlIDs.Get( ControlIDs::idBtnSearchWorkspaceFiles ), ThreadSearchView::OnBtnSearchWorkspaceFiles )
  EVT_TOGGLEBUTTON( controlIDs.Get( ControlIDs::idBtnSearchDirectoryFiles ), ThreadSearchView::OnBtnSearchDirectoryFiles )
  EVT_TIMER( controlIDs.Get( ControlIDs::idTmrListCtrlUpdate ), ThreadSearchView::OnTmrListCtrlUpdate )
END_EVENT_TABLE();


void ThreadSearchView::OnThreadSearchErrorEvent( const ThreadSearchEvent& event ) {
  Manager::Get()->GetLogManager()->LogError( F( _T( "文件丢失: %s" ), event.GetString().wx_str() ) );
}

void ThreadSearchView::OnCboSearchExprEnter( wxCommandEvent & ) {
  ThreadSearchFindData findData = m_ThreadSearchPlugin.GetFindData();
  findData.SetFindText( m_pCboSearchExpr->GetValue() );
  ThreadedSearch( findData );
}

void ThreadSearchView::OnBtnSearchClick( wxCommandEvent & ) {
  if( m_MutexSearchEventsArray.Lock() == wxMUTEX_NO_ERROR ) {
    int nbEvents = m_ThreadSearchEventsArray.GetCount();
    m_MutexSearchEventsArray.Unlock();
    if( m_pFindThread != NULL ) {
      UpdateSearchButtons( false );
      StopThread();
    } else if( nbEvents > 0 ) {
      UpdateSearchButtons( false );
      if( ClearThreadSearchEventsArray() == false ) {
        msging( _T( "Failed to clear events array." ), _T( "错误" ), wxICON_ERROR );
      }
    } else {
      ThreadSearchFindData findData = m_ThreadSearchPlugin.GetFindData();
      findData.SetFindText( m_pCboSearchExpr->GetValue() );
      ThreadedSearch( findData );
    }
  }
}

void ThreadSearchView::OnBtnOptionsClick( wxCommandEvent & ) {
  wxMenu menu;
  menu.Append( controlIDs.Get( ControlIDs::idOptionDialog ), _T( "选项" ) );
  menu.AppendCheckItem( controlIDs.Get( ControlIDs::idOptionWholeWord ), _T( "严谨匹配" ) );
  menu.AppendCheckItem( controlIDs.Get( ControlIDs::idOptionStartWord ), _T( "首单词匹配" ) );
  menu.AppendCheckItem( controlIDs.Get( ControlIDs::idOptionMatchCase ), _T( "区分大小写" ) );
  menu.AppendCheckItem( controlIDs.Get( ControlIDs::idOptionRegEx ), _T( "正则表达式" ) );
  PopupMenu( &menu );
}

void ThreadSearchView::OnShowOptionsDialog( wxCommandEvent & ) {
  cbConfigurationDialog* pDlg = new cbConfigurationDialog( Manager::Get()->GetAppWindow(), -1, _T( "Options" ) );
  ThreadSearchConfPanel* pConfPanel = new ThreadSearchConfPanel( m_ThreadSearchPlugin, pDlg );
  pDlg->AttachConfigurationPanel( pConfPanel );
  pDlg->ShowModal();
  pDlg->Destroy();
}

void ThreadSearchView::OnQuickOptions( wxCommandEvent &event ) {
  ThreadSearchFindData findData = m_ThreadSearchPlugin.GetFindData();
  bool hasChange = false;
  if( event.GetId() == controlIDs.Get( ControlIDs::idOptionWholeWord ) ) {
    findData.SetMatchWord( event.IsChecked() );
    hasChange = true;
  } else if( event.GetId() == controlIDs.Get( ControlIDs::idOptionStartWord ) ) {
    findData.SetStartWord( event.IsChecked() );
    hasChange = true;
  } else if( event.GetId() == controlIDs.Get( ControlIDs::idOptionMatchCase ) ) {
    findData.SetMatchCase( event.IsChecked() );
    hasChange = true;
  } else if( event.GetId() == controlIDs.Get( ControlIDs::idOptionRegEx ) ) {
    findData.SetRegEx( event.IsChecked() );
    hasChange = true;
  }
  if( hasChange ) {
    m_ThreadSearchPlugin.SetFindData( findData );
    UpdateOptionsButtonImage( findData );
  }
}

void ThreadSearchView::UpdateOptionsButtonImage( const ThreadSearchFindData &findData ) {
  wxString name = ( findData.IsOptionEnabled() ? _T( "optionsactive.png" ) : _T( "options.png" ) );
  wxBitmap bitmap( GetImagePrefix() + name, wxBITMAP_TYPE_PNG );
  m_pBtnOptions->SetBitmapLabel( bitmap );
}

void ThreadSearchView::OnQuickOptionsUpdateUI( wxUpdateUIEvent &event ) {
  ThreadSearchFindData &findData = m_ThreadSearchPlugin.GetFindData();
  if( event.GetId() == controlIDs.Get( ControlIDs::idOptionWholeWord ) ) {
    event.Check( findData.GetMatchWord() );
  } else if( event.GetId() == controlIDs.Get( ControlIDs::idOptionStartWord ) ) {
    event.Check( findData.GetStartWord() );
  } else if( event.GetId() == controlIDs.Get( ControlIDs::idOptionMatchCase ) ) {
    event.Check( findData.GetMatchCase() );
  } else if( event.GetId() == controlIDs.Get( ControlIDs::idOptionRegEx ) ) {
    event.Check( findData.GetRegEx() );
  }
}

void ThreadSearchView::OnBtnShowDirItemsClick( wxCommandEvent& ( event ) ) {
  wxSizer* pTopSizer = GetSizer();
  wxASSERT( m_pSizerSearchDirItems && pTopSizer );
  bool show = !m_pPnlDirParams->IsShown();
  m_ThreadSearchPlugin.SetShowDirControls( show );
  pTopSizer->Show( m_pSizerSearchDirItems, show, true );
  if( show == true ) {
    m_pBtnShowDirItems->SetToolTip( _T( "Hide dir items" ) );
  } else {
    m_pBtnShowDirItems->SetToolTip( _T( "Show dir items" ) );
  }
  pTopSizer->Layout();
}

void ThreadSearchView::OnSplitterDoubleClick( wxSplitterEvent & ) {
  m_ThreadSearchPlugin.SetShowCodePreview( false );
  ApplySplitterSettings( false, m_pSplitter->GetSplitMode() );
  msging( _T( "重新启用代码预览, 选中\"线程搜索选项\"面板中的\"显示代码预览编辑器\"." ),
          _T( "线程搜索信息" ), wxICON_INFORMATION );
}

void ThreadSearchView::set_properties() {
  const wxString &prefix = GetImagePrefix();
  SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
  m_pCboSearchExpr->SetMinSize( wxSize( 180, -1 ) );
  m_pBtnSearch->SetToolTip( _T( "文件中搜索" ) );
  m_pBtnSearch->SetBitmapDisabled( wxBitmap( prefix + _T( "findfdisabled.png" ), wxBITMAP_TYPE_PNG ) );
  m_pBtnSearch->SetSize( m_pBtnSearch->GetBestSize() );
  m_pBtnOptions->SetToolTip( _T( "选项" ) );
  m_pBtnOptions->SetBitmapDisabled( wxBitmap( prefix + _T( "optionsdisabled.png" ), wxBITMAP_TYPE_PNG ) );
  m_pBtnOptions->SetSize( m_pBtnOptions->GetBestSize() );
  m_pBtnShowDirItems->SetToolTip( _T( "目录参数" ) );
  m_pBtnShowDirItems->SetBitmapDisabled( wxBitmap( prefix + _T( "showdirdisabled.png" ), wxBITMAP_TYPE_PNG ) );
  m_pBtnShowDirItems->SetSize( m_pBtnShowDirItems->GetBestSize() );
  m_pPnlPreview->SetMinSize( wxSize( 25, -1 ) );
  m_pSearchPreview->SetReadOnly( true );
  ThreadSearchFindData findData;
  m_ThreadSearchPlugin.GetFindData( findData );
  m_pPnlDirParams->SetSearchDirHidden( findData.GetHiddenSearch() );
  m_pPnlDirParams->SetSearchDirRecursively( findData.GetRecursiveSearch() );
  m_pPnlDirParams->SetSearchDirPath( findData.GetSearchPath() );
  m_pPnlDirParams->SetSearchMask( findData.GetSearchMask() );
  m_pPnlSearchIn->SetSearchInOpenFiles( findData.MustSearchInOpenFiles() );
  m_pPnlSearchIn->SetSearchInTargetFiles( findData.MustSearchInTarget() );
  m_pPnlSearchIn->SetSearchInProjectFiles( findData.MustSearchInProject() );
  m_pPnlSearchIn->SetSearchInWorkspaceFiles( findData.MustSearchInWorkspace() );
  m_pPnlSearchIn->SetSearchInDirectory( findData.MustSearchInDirectory() );
  UpdateOptionsButtonImage( findData );
}

void ThreadSearchView::do_layout() {
  wxBoxSizer* m_pSizerTop = new wxBoxSizer( wxVERTICAL );
  wxBoxSizer* m_pSizerSplitter = new wxBoxSizer( wxHORIZONTAL );
  wxBoxSizer* m_pSizerListLog = new wxBoxSizer( wxHORIZONTAL );
  wxBoxSizer* m_pSizerSearchPreview = new wxBoxSizer( wxHORIZONTAL );
  m_pSizerSearchDirItems = new wxStaticBoxSizer( m_pSizerSearchDirItems_staticbox, wxHORIZONTAL );
  m_pSizerSearchItems = new wxBoxSizer( wxHORIZONTAL );
  m_pSizerSearchItems->Add( m_pCboSearchExpr, 2, wxALL | wxALIGN_CENTER_VERTICAL | wxADJUST_MINSIZE, 4 );
  m_pSizerSearchItems->Add( m_pBtnSearch, 0, wxALL | wxALIGN_CENTER_VERTICAL | wxADJUST_MINSIZE, 4 );
  m_pSizerSearchItems->Add( m_pBtnOptions, 0, wxALL | wxALIGN_CENTER_VERTICAL | wxADJUST_MINSIZE, 4 );
  m_pSizerSearchItems->Add( m_pStaticLine1, 0, wxLEFT | wxRIGHT | wxEXPAND, 2 );
  m_pSizerSearchItems->Add( m_pStaTxtSearchIn, 0, wxALL | wxALIGN_CENTER_VERTICAL | wxADJUST_MINSIZE, 4 );
  m_pSizerSearchItems->Add( m_pPnlSearchIn, 0, wxALIGN_CENTER_VERTICAL | wxADJUST_MINSIZE, 0 );
  m_pSizerSearchItems->Add( m_pStaticLine2, 0, wxLEFT | wxRIGHT | wxEXPAND, 2 );
  m_pSizerSearchItems->Add( m_pBtnShowDirItems, 0, wxALL | wxALIGN_CENTER_VERTICAL | wxADJUST_MINSIZE, 4 );
  m_pSizerTop->Add( m_pSizerSearchItems, 0, wxEXPAND, 0 );
  m_pSizerSearchDirItems->Add( m_pPnlDirParams, 1, wxALIGN_CENTER_VERTICAL, 0 );
  m_pSizerTop->Add( m_pSizerSearchDirItems, 0, wxBOTTOM | wxEXPAND, 4 );
  m_pSizerSearchPreview->Add( m_pSearchPreview, 1, wxEXPAND | wxADJUST_MINSIZE, 0 );
  m_pPnlPreview->SetAutoLayout( true );
  m_pPnlPreview->SetSizer( m_pSizerSearchPreview );
  m_pSizerListLog->Add( m_pLogger->GetWindow(), 1, wxEXPAND | wxFIXED_MINSIZE, 0 );
  m_pPnlListLog->SetAutoLayout( true );
  m_pPnlListLog->SetSizer( m_pSizerListLog );
  m_pSplitter->SplitVertically( m_pPnlPreview, m_pPnlListLog );
  m_pSizerSplitter->Add( m_pSplitter, 1, wxEXPAND | wxADJUST_MINSIZE, 0 );
  m_pSizerTop->Add( m_pSizerSplitter, 1, wxEXPAND, 0 );
  SetAutoLayout( true );
  SetSizer( m_pSizerTop );
  m_pSizerTop->Fit( this );
  m_pSizerTop->SetSizeHints( this );
  m_pSplitter->SetMinimumPaneSize( 50 );
}

void ThreadSearchView::OnThreadExit() {
  if( m_pFindThread != NULL ) {
    m_pFindThread = NULL;
  }
  if( m_StoppingThread > 0 ) {
    m_StoppingThread--;
  }
}

void ThreadSearchView::ThreadedSearch( const ThreadSearchFindData& aFindData ) {
  if( aFindData.GetFindText() != wxEmptyString ) {
    ThreadSearchFindData findData( aFindData );
    m_pLogger->OnSearchBegin( aFindData );
    m_pFindThread = new ThreadSearchThread( this, findData );
    if( m_pFindThread != NULL ) {
      if( m_pFindThread->Create() == wxTHREAD_NO_ERROR ) {
        if( m_pFindThread->Run() != wxTHREAD_NO_ERROR ) {
          m_pFindThread->Delete();
          m_pFindThread = NULL;
          msging( _T( "无法运行搜索线程" ) );
        } else {
          AddExpressionToSearchCombos( findData.GetFindText(), findData.GetSearchPath(),
                                       findData.GetSearchMask() );
          UpdateSearchButtons( true, cancel );
          EnableControls( false );
          m_Timer.Start( TIMER_PERIOD, wxTIMER_CONTINUOUS );
        }
      } else {
        m_pFindThread->Delete();
        m_pFindThread = NULL;
        msging( _T( "未能创建搜索线程 (2)" ) );
      }
    } else {
      msging( _T( "未能创建搜索线程 (1)" ) );
    }
  }
}

bool ThreadSearchView::UpdatePreview( const wxString& file, long line ) {
  bool success( true );
  if( line > 0 ) {
    line--;
  }
  m_pSearchPreview->Enable( false );
  m_pSearchPreview->SetReadOnly( false );
  wxFileName filename( file );
  if( ( m_PreviewFilePath != file ) || ( m_PreviewFileDate != filename.GetModificationTime() ) ) {
    ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "editor" ) );
    m_PreviewFilePath = file;
    m_PreviewFileDate = filename.GetModificationTime();
    EncodingDetector enc( m_PreviewFilePath, false );
    success = enc.IsOK();
    m_pSearchPreview->SetText( enc.GetWxStr() );
    M_Code_Editor::ApplyStyles( m_pSearchPreview );
    EditorColourSet EdColSet;
    EdColSet.Apply( EdColSet.GetLanguageForFilename( m_PreviewFilePath ), m_pSearchPreview );
    SetFoldingIndicator();
    UnderlineFoldedLines( mgr->ReadBool( _T( "/folding/underline_folded_line" ), true ) );
  }
  if( success == true ) {
    int onScreen = m_pSearchPreview->LinesOnScreen() >> 1;
    m_pSearchPreview->GotoLine( line - onScreen );
    m_pSearchPreview->GotoLine( line + onScreen );
    m_pSearchPreview->GotoLine( line );
    m_pSearchPreview->EnsureVisible( line );
    int startPos = m_pSearchPreview->PositionFromLine( line );
    int endPos   = m_pSearchPreview->GetLineEndPosition( line );
    m_pSearchPreview->SetSelectionVoid( endPos, startPos );
  }
  m_pSearchPreview->SetReadOnly( true );
  m_pSearchPreview->Enable( true );
  return success;
}

void ThreadSearchView::OnLoggerClick( const wxString& file, long line ) {
  UpdatePreview( file, line );
}

void ThreadSearchView::OnLoggerDoubleClick( const wxString& file, long line ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->Open( file );
  if( !line || !ed ) {
    return;
  }
  line -= 1;
  ed->Activate();
  ed->GotoLine( line );
  if( M_Code_StyledTextCtrl* control = ed->GetControl() ) {
    control->EnsureVisible( line );
    wxFocusEvent ev( wxEVT_SET_FOCUS );
    ev.SetWindow( this );
    control->AddPendingEvent( ev );
  }
}

void ThreadSearchView::OnMarginClick( wxScintillaEvent& event ) {
  switch( event.GetMargin() ) {
  case 2: {
    int lineYpix = event.GetPosition();
    int line = m_pSearchPreview->LineFromPosition( lineYpix );
    m_pSearchPreview->ToggleFold( line );
    break;
  }
  case 1: {
    break;
  }
  default: {
    break;
  }
  }
}

void ThreadSearchView::OnContextMenu( wxContextMenuEvent& event ) {
  event.StopPropagation();
}

void ThreadSearchView::AddExpressionToSearchCombos( const wxString& expression, const wxString& path, const wxString& mask ) {
  int index = m_pCboSearchExpr->FindString( expression );
  if( index != wxNOT_FOUND ) {
    m_pCboSearchExpr->Delete( index );
  }
  if( m_pCboSearchExpr->GetCount() > MAX_NB_SEARCH_ITEMS ) {
    m_pCboSearchExpr->Delete( m_pCboSearchExpr->GetCount() - 1 );
  }
  m_pCboSearchExpr->Insert( expression, 0 );
  m_pCboSearchExpr->SetSelection( 0 );
  m_pPnlDirParams->AddExpressionToCombos( path, mask );
}

void ThreadSearchView::FocusSearchCombo( const wxString &searchWord ) {
  if( !searchWord.empty() ) {
    m_pCboSearchExpr->SetValue( searchWord );
  }
  m_pCboSearchExpr->SetFocus();
}

void ThreadSearchView::Update() {
  ThreadSearchFindData findData;
  m_ThreadSearchPlugin.GetFindData( findData );
  m_pPnlSearchIn->SetSearchInOpenFiles( findData.MustSearchInOpenFiles() );
  m_pPnlSearchIn->SetSearchInTargetFiles( findData.MustSearchInTarget() );
  m_pPnlSearchIn->SetSearchInProjectFiles( findData.MustSearchInProject() );
  m_pPnlSearchIn->SetSearchInWorkspaceFiles( findData.MustSearchInWorkspace() );
  m_pPnlSearchIn->SetSearchInDirectory( findData.MustSearchInDirectory() );
  m_pPnlDirParams->SetSearchDirHidden( findData.GetHiddenSearch() );
  m_pPnlDirParams->SetSearchDirRecursively( findData.GetRecursiveSearch() );
  m_pPnlDirParams->SetSearchDirPath( findData.GetSearchPath() );
  m_pPnlDirParams->SetSearchMask( findData.GetSearchMask() );
  ShowSearchControls( m_ThreadSearchPlugin.GetShowSearchControls() );
  SetLoggerType( m_ThreadSearchPlugin.GetLoggerType() );
  m_pLogger->Update();
  ApplySplitterSettings( m_ThreadSearchPlugin.GetShowCodePreview(), m_ThreadSearchPlugin.GetSplitterMode() );
}

void ThreadSearchView::OnBtnSearchOpenFiles( wxCommandEvent &event ) {
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeOpenFiles, m_pPnlSearchIn->GetSearchInOpenFiles() );
  event.Skip();
}

void ThreadSearchView::OnBtnSearchTargetFiles( wxCommandEvent &event ) {
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeTargetFiles, m_pPnlSearchIn->GetSearchInTargetFiles() );
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeProjectFiles, false );
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeWorkspaceFiles, false );
  event.Skip();
}

void ThreadSearchView::OnBtnSearchProjectFiles( wxCommandEvent &event ) {
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeProjectFiles, m_pPnlSearchIn->GetSearchInProjectFiles() );
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeTargetFiles, false );
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeWorkspaceFiles, false );
  event.Skip();
}

void ThreadSearchView::OnBtnSearchWorkspaceFiles( wxCommandEvent &event ) {
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeWorkspaceFiles, m_pPnlSearchIn->GetSearchInWorkspaceFiles() );
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeTargetFiles, false );
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeProjectFiles, false );
  event.Skip();
}

void ThreadSearchView::OnBtnSearchDirectoryFiles( wxCommandEvent &event ) {
  m_ThreadSearchPlugin.GetFindData().UpdateSearchScope( ScopeDirectoryFiles, m_pPnlSearchIn->GetSearchInDirectory() );
  event.Skip();
}

void ThreadSearchView::EnableControls( bool enable ) {
  ControlIDs::IDs idsArray[] = {
    ControlIDs::idBtnDirSelectClick,
    ControlIDs::idBtnOptions,
    ControlIDs::idCboSearchExpr,
    ControlIDs::idChkSearchDirRecurse,
    ControlIDs::idChkSearchDirHidden,
    ControlIDs::idBtnSearchOpenFiles,
    ControlIDs::idBtnSearchTargetFiles,
    ControlIDs::idBtnSearchProjectFiles,
    ControlIDs::idBtnSearchWorkspaceFiles,
    ControlIDs::idBtnSearchDirectoryFiles,
    ControlIDs::idSearchDirPath,
    ControlIDs::idSearchMask
  };
  for( unsigned int i = 0; i < sizeof( idsArray ) / sizeof( idsArray[0] ); ++i ) {
    wxWindow* pWnd = wxWindow::FindWindow( controlIDs.Get( idsArray[i] ) );
    if( pWnd != 0 ) {
      pWnd->Enable( enable );
    } else {
      msging( F( _T( "无法启用窗口 (id=%ld)" ), idsArray[i] ).c_str(), _T( "错误" ), wxOK | wxICON_ERROR, this );
    }
  }
}

void ThreadSearchView::PostThreadSearchEvent( const ThreadSearchEvent& event ) {
  if( m_MutexSearchEventsArray.Lock() == wxMUTEX_NO_ERROR ) {
    m_ThreadSearchEventsArray.Add( event.Clone() );
    m_MutexSearchEventsArray.Unlock();
  }
}

void ThreadSearchView::OnTmrListCtrlUpdate( wxTimerEvent& ) {
  if( m_MutexSearchEventsArray.Lock() == wxMUTEX_NO_ERROR ) {
    if( m_ThreadSearchEventsArray.GetCount() > 0 ) {
      ThreadSearchEvent *pEvent = static_cast<ThreadSearchEvent*>( m_ThreadSearchEventsArray[0] );
      m_pLogger->OnThreadSearchEvent( *pEvent );
      delete pEvent;
      m_ThreadSearchEventsArray.RemoveAt( 0, 1 );
    }
    if( ( m_ThreadSearchEventsArray.GetCount() == 0 ) && ( m_pFindThread == NULL ) ) {
      m_Timer.Stop();
      m_pLogger->OnSearchEnd();
      UpdateSearchButtons( true, search );
      EnableControls( true );
    }
    m_MutexSearchEventsArray.Unlock();
  }
}

bool ThreadSearchView::ClearThreadSearchEventsArray() {
  bool success = ( m_MutexSearchEventsArray.Lock() == wxMUTEX_NO_ERROR );
  if( success == true ) {
    size_t i                  = m_ThreadSearchEventsArray.GetCount();
    ThreadSearchEvent* pEvent = NULL;
    while( i != 0 ) {
      pEvent = static_cast<ThreadSearchEvent*>( m_ThreadSearchEventsArray[0] );
      delete pEvent;
      m_ThreadSearchEventsArray.RemoveAt( 0, 1 );
      i--;
    }
    m_MutexSearchEventsArray.Unlock();
  }
  return success;
}

bool ThreadSearchView::StopThread() {
  bool success = false;
  if( ( m_StoppingThread == 0 ) && ( m_pFindThread != NULL ) ) {
    m_StoppingThread++;
    m_pFindThread->Delete();
    m_Timer.Stop();
    wxThread::Sleep( 2 * TIMER_PERIOD );
    success = ClearThreadSearchEventsArray();
    if( success == false ) {
      msging( _T( "清除事件数组失败." ), _T( "错误" ), wxICON_ERROR );
    }
    UpdateSearchButtons( true, search );
    EnableControls( true );
  }
  return success;
}

bool ThreadSearchView::IsSearchRunning() {
  bool searchRunning = ( m_pFindThread != 0 );
  if( m_MutexSearchEventsArray.Lock() == wxMUTEX_NO_ERROR ) {
    searchRunning = searchRunning || ( m_ThreadSearchEventsArray.GetCount() > 0 );
    m_MutexSearchEventsArray.Unlock();
  }
  return searchRunning;
}

void ThreadSearchView::UpdateSearchButtons( bool enable, eSearchButtonLabel label ) {
  wxString searchButtonLabels[] = {_T( "Search" ), _T( "Cancel search" ), wxEmptyString};
  wxString prefix = ConfigManager::GetDataFolder() + _T( "/images/ThreadSearch/" )  ;
  wxString searchButtonPathsEnabled[]  = {prefix + _T( "findf.png" ),
                                          prefix + _T( "stop.png" ),
                                          wxEmptyString
                                         };
  wxString searchButtonPathsDisabled[] = {prefix + _T( "findfdisabled.png" ),
                                          prefix + _T( "stopdisabled.png" ),
                                          wxEmptyString
                                         };
  if( label != skip ) {
    m_pBtnSearch->SetToolTip( searchButtonLabels[label] );
    m_pBtnSearch->SetBitmapLabel( wxBitmap( searchButtonPathsEnabled [label], wxBITMAP_TYPE_PNG ) );
    m_pBtnSearch->SetBitmapDisabled( wxBitmap( searchButtonPathsDisabled[label], wxBITMAP_TYPE_PNG ) );
  }
  m_pBtnSearch->Enable( enable );
}

wxString ThreadSearchView::GetImagePrefix() const {
  return ConfigManager::GetDataFolder() + _T( "/images/ThreadSearch/" );
}

void ThreadSearchView::ShowSearchControls( bool show ) {
  bool redraw = false;
  wxSizer* pTopSizer = GetSizer();
  m_ThreadSearchPlugin.SetShowSearchControls( show );
  if( m_pBtnSearch->IsShown() != show ) {
    pTopSizer->Show( m_pSizerSearchItems, show, true );
    redraw = true;
  }
  if( show == true ) {
    show = m_ThreadSearchPlugin.GetShowDirControls();
  }
  if( m_pPnlDirParams->IsShown() != show ) {
    pTopSizer->Show( m_pSizerSearchDirItems, show, true );
    redraw = true;
  }
  if( redraw == true ) {
    pTopSizer->Layout();
  }
}

void ThreadSearchView::ApplySplitterSettings( bool showCodePreview, long splitterMode ) {
  if( showCodePreview == true ) {
    if( ( m_pSplitter->IsSplit() == false ) || ( splitterMode != m_pSplitter->GetSplitMode() ) ) {
      if( m_pSplitter->IsSplit() == true ) {
        m_pSplitter->Unsplit();
      }
      if( splitterMode == wxSPLIT_HORIZONTAL ) {
        m_pSplitter->SplitHorizontally( m_pPnlListLog, m_pPnlPreview );
      } else {
        m_pSplitter->SplitVertically( m_pPnlPreview, m_pPnlListLog );
      }
    }
  } else {
    if( m_pSplitter->IsSplit() == true ) {
      m_pSplitter->Unsplit( m_pPnlPreview );
    }
  }
}

void ThreadSearchView::SetLoggerType( ThreadSearchLoggerBase::eLoggerTypes lgrType ) {
  if( lgrType != m_pLogger->GetLoggerType() ) {
    delete m_pLogger;
    m_pLogger = ThreadSearchLoggerBase::BuildThreadSearchLoggerBase( *this , m_ThreadSearchPlugin
                , lgrType , m_ThreadSearchPlugin.GetFileSorting() , m_pPnlListLog , controlIDs.Get( ControlIDs::idWndLogger ) );
    m_pPnlListLog->GetSizer()->Add( m_pLogger->GetWindow(), 1, wxEXPAND | wxFIXED_MINSIZE, 0 );
    wxSizer* pTopSizer = m_pPnlListLog->GetSizer();
    pTopSizer->Layout();
  }
}

void ThreadSearchView::SetSashPosition( int position, const bool redraw ) {
  m_pSplitter->SetSashPosition( position, redraw );
}

int ThreadSearchView::GetSashPosition() const {
  return m_pSplitter->GetSashPosition();
}

void ThreadSearchView::SetSearchHistory( const wxArrayString& searchPatterns, const wxArrayString& searchDirs,
    const wxArrayString& searchMasks ) {
  m_pCboSearchExpr->Append( searchPatterns );
  if( searchPatterns.GetCount() > 0 ) {
    m_pCboSearchExpr->SetSelection( 0 );
  }
  m_pPnlDirParams->SetSearchHistory( searchDirs, searchMasks );
}

wxArrayString ThreadSearchView::GetSearchHistory() const {
  return m_pCboSearchExpr->GetStrings();
}

wxArrayString ThreadSearchView::GetSearchDirsHistory() const {
  return m_pPnlDirParams->GetSearchDirsHistory();
}

wxArrayString ThreadSearchView::GetSearchMasksHistory() const {
  return m_pPnlDirParams->GetSearchMasksHistory();
}

void ThreadSearchView::SetMarkerStyle( int marker, int markerType, wxColor fore, wxColor back ) {
  m_pSearchPreview->MarkerDefine( marker, markerType );
  m_pSearchPreview->MarkerSetForeground( marker, fore );
  m_pSearchPreview->MarkerSetBackground( marker, back );
}

void ThreadSearchView::UnderlineFoldedLines( bool underline ) {
  m_pSearchPreview->SetFoldFlags( underline ? 16 : 0 );
}

void ThreadSearchView::SetFoldingIndicator() {
  SetMarkerStyle( wxSCI_MARKNUM_FOLDEROPEN, wxSCI_MARK_CIRCLEMINUS, wxColor( 0xff, 0xff, 0xff ), wxColor( 0x80, 0x80, 0x80 ) );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDER, wxSCI_MARK_CIRCLEPLUS, wxColor( 0xff, 0xff, 0xff ), wxColor( 0x80, 0x80, 0x80 ) );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDERSUB, wxSCI_MARK_VLINE, wxColor( 0xff, 0xff, 0xff ), wxColor( 0x80, 0x80, 0x80 ) );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDERTAIL, wxSCI_MARK_LCORNERCURVE, wxColor( 0xff, 0xff, 0xff ), wxColor( 0x80, 0x80, 0x80 ) );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDEREND, wxSCI_MARK_CIRCLEPLUSCONNECTED, wxColor( 0xff, 0xff, 0xff ), wxColor( 0x80, 0x80, 0x80 ) );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDEROPENMID, wxSCI_MARK_CIRCLEMINUSCONNECTED, wxColor( 0xff, 0xff, 0xff ), wxColor( 0x80, 0x80, 0x80 ) );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDERMIDTAIL, wxSCI_MARK_TCORNER, wxColor( 0xff, 0xff, 0xff ), wxColor( 0x80, 0x80, 0x80 ) );
}
